package com.btm.easymetadata.server.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.btm.easymetadata.server.entity.dao.DataField;
import com.btm.easymetadata.server.entity.dao.LinkField;
import com.btm.easymetadata.server.entity.dto.LinkDataFieldDTO;
import com.btm.easymetadata.server.entity.vo.DataFieldVO;
import com.btm.easymetadata.server.mapper.mybatisplus.DataFieldMapper;
import com.btm.easymetadata.server.mapper.mybatisplus.LinkFieldMapper;
import com.btm.easymetadata.server.util.DAOtoVOUtil;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class LinkFieldService {

    private final LinkFieldMapper linkFieldMapper;
    private final DataFieldMapper dataFieldMapper;

    public List<DataFieldVO> selectByFrom(Long fieldId) {
        LambdaQueryWrapper<LinkField> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LinkField::getFieldFrom, fieldId);
        List<LinkField> linkFields = linkFieldMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(linkFields)) {
            return Collections.emptyList();
        }
        List<Long> fieldIds = linkFields.stream().map(LinkField::getFieldTo).collect(Collectors.toList());
        LambdaQueryWrapper<DataField> dataFieldWrapper = new LambdaQueryWrapper<>();
        dataFieldWrapper.in(DataField::getId, fieldIds);
        List<DataField> dataFields = dataFieldMapper.selectList(dataFieldWrapper);
        return dataFields.stream().map(DAOtoVOUtil.INSTANCE::getDataFieldFromDTO).collect(Collectors.toList());
    }

    public List<DataFieldVO> selectByTo(Long fieldId) {
        LambdaQueryWrapper<LinkField> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LinkField::getFieldTo, fieldId);
        List<LinkField> linkFields = linkFieldMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(linkFields)) {
            return Collections.emptyList();
        }
        List<Long> fieldIds = linkFields.stream().map(LinkField::getFieldFrom).collect(Collectors.toList());
        LambdaQueryWrapper<DataField> dataFieldWrapper = new LambdaQueryWrapper<>();
        dataFieldWrapper.in(DataField::getId, fieldIds);
        List<DataField> dataFields = dataFieldMapper.selectList(dataFieldWrapper);
        return dataFields.stream().map(DAOtoVOUtil.INSTANCE::getDataFieldFromDTO).collect(Collectors.toList());
    }

    public boolean addLink(Long fromId, Long toId) {
        if (fromId.equals(toId)) {
            return false;
        }
        // 查询从fromId到toId是否可能成环
        List<Long> fieldIds = new LinkedList<>();
        fieldIds.add(toId);
        while (true) {
            if (CollectionUtils.isEmpty(fieldIds)) {
                break;
            }
            List<Map<String, BigInteger>> maps = linkFieldMapper.selectLinked(fieldIds);
            if (CollectionUtils.isEmpty(maps)) {
                break;
            }
            fieldIds.clear();
            for (Map<String, BigInteger> map : maps) {
                for (Map.Entry<String, BigInteger> entry : map.entrySet()) {
                    if (Objects.nonNull(entry.getValue())) {
                        if (BigInteger.valueOf(fromId).equals(entry.getValue())) {
                            return false;
                        } else {
                            fieldIds.add(entry.getValue().longValue());
                        }
                    }
                }
            }
        }

        // 检查通过未成环，则可以写入
        LinkField linkField = new LinkField();
        linkField.setFieldTo(toId);
        linkField.setFieldFrom(fromId);
        return 1 == linkFieldMapper.insert(linkField);
    }

    /**
     * 返回与指定fieldId未直接关联的字段信息
     * @param linkDataFieldDTO
     * @return
     */
    public List<DataFieldVO> selectLinkable(LinkDataFieldDTO linkDataFieldDTO) {
        List<DataField> dataFields = linkFieldMapper.selectLinkable(linkDataFieldDTO);
        if (CollectionUtils.isEmpty(dataFields)) {
            return Collections.emptyList();
        } else {
            return dataFields.stream().map(DAOtoVOUtil.INSTANCE::getDataFieldFromDTO).collect(Collectors.toList());
        }
    }
}
